package org.example.utils;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.CharArrayWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class IOUtils
{
	public static final char DIR_SEPARATOR_UNIX = '/';
	public static final char DIR_SEPARATOR_WINDOWS = '\\';
	public static final char DIR_SEPARATOR = File.separatorChar;
	public static final String LINE_SEPARATOR_UNIX = "\n";
	public static final String LINE_SEPARATOR_WINDOWS = "\r\n";

	public static final String LINE_SEPARATOR;
	static
	{
		StringWriter buf = new StringWriter(4);
		PrintWriter out = new PrintWriter(buf);
		out.println();
		LINE_SEPARATOR = buf.toString();
	}

	private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;

	public IOUtils()
	{
		super();
	}

	public static void closeQuietly(Reader input)
	{
		try
		{
			if (input != null)
				input.close();
		}
		catch (IOException ioe)
		{
		}
	}

	public static void closeQuietly(Writer output)
	{
		try
		{
			if (output != null)
				output.close();
		}
		catch (IOException ioe)
		{
		}
	}

	public static void closeQuietly(InputStream input)
	{
		try
		{
			if (input != null)
				input.close();
		}
		catch (IOException ioe)
		{
		}
	}

	public static void closeQuietly(OutputStream output)
	{
		try
		{
			if (output != null)
				output.close();
		}
		catch (IOException ioe)
		{
		}
	}

	public static byte[] toByteArray(InputStream input) throws IOException
	{
		ByteArrayOutputStream output = new ByteArrayOutputStream();
		copy(input, output);
		return output.toByteArray();
	}

	public static byte[] toByteArrayQuietly(InputStream input)
	{
		ByteArrayOutputStream output = new ByteArrayOutputStream();
		try
		{
			copy(input, output);
			return output.toByteArray();
		}
		catch( IOException err )
		{
			throw new RuntimeException( err ) ;
		}
	}

	public static byte[] toByteArray(Reader input) throws IOException
	{
		ByteArrayOutputStream output = new ByteArrayOutputStream();
		copy(input, output);
		return output.toByteArray();
	}

	public static byte[] toByteArray(Reader input, String encoding)
			throws IOException
	{
		ByteArrayOutputStream output = new ByteArrayOutputStream();
		copy(input, output, encoding);
		return output.toByteArray();
	}

	public static char[] toCharArray(InputStream is) throws IOException
	{
		CharArrayWriter output = new CharArrayWriter();
		copy(is, output);
		return output.toCharArray();
	}

	public static char[] toCharArray(InputStream is, String encoding)
			throws IOException
	{
		CharArrayWriter output = new CharArrayWriter();
		copy(is, output, encoding);
		return output.toCharArray();
	}

	public static char[] toCharArray(Reader input) throws IOException
	{
		CharArrayWriter sw = new CharArrayWriter();
		copy(input, sw);
		return sw.toCharArray();
	}

	public static String toString(InputStream input) throws IOException
	{
		StringWriter sw = new StringWriter();
		copy(input, sw);
		return sw.toString();
	}

	public static String toStringQuietly(InputStream input)
	{
		StringWriter sw = new StringWriter();
		try
		{
			copy(input, sw);
			return sw.toString();
		}
		catch (IOException err)
		{
			throw new RuntimeException( err ) ;
		}
	}

	public static String toString(InputStream input, String encoding)
			throws IOException
	{
		StringWriter sw = new StringWriter();
		copy(input, sw, encoding);
		return sw.toString();
	}

	public static String toStringQuietly(InputStream input, String encoding)
	{
		StringWriter sw = new StringWriter();
		try
		{
			copy(input, sw, encoding);
			return sw.toString();
		}
		catch (IOException err)
		{
			throw new RuntimeException( err ) ;
		}
	}

	public static String toString(Reader input) throws IOException
	{
		StringWriter sw = new StringWriter();
		copy(input, sw);
		return sw.toString();
	}

	public static String toString(byte[] input, String encoding)
			throws IOException
	{
		if (encoding == null)
			return new String(input);
		else
			return new String(input, encoding);
	}

	public static List<String> readLines(InputStream input) throws IOException
	{
		InputStreamReader reader = new InputStreamReader(input);
		return readLines(reader);
	}

	public static List<String> readLines(InputStream input, String encoding)
			throws IOException
	{
		if (encoding == null)
			return readLines(input);
		else
		{
			InputStreamReader reader = new InputStreamReader(input, encoding);
			return readLines(reader);
		}
	}

	public static List<String> readLines(Reader input) throws IOException
	{
		BufferedReader reader = new BufferedReader(input);
		List<String> list = new ArrayList<String>();
		String line = reader.readLine();
		while (line != null)
		{
			list.add(line);
			line = reader.readLine();
		}
		return list;
	}

	public static InputStream toInputStream(String input)
	{
		byte[] bytes = input.getBytes();
		return new ByteArrayInputStream(bytes);
	}

	public static InputStream toInputStream(String input, String encoding)
			throws IOException
	{
		byte[] bytes = encoding != null ? input.getBytes(encoding) : input
				.getBytes();
		return new ByteArrayInputStream(bytes);
	}

	public static InputStream toInputStreamQuietly(String input, String encoding)
	{
		try
		{
			byte[] bytes = encoding != null ? input.getBytes(encoding) : input
					.getBytes();
			return new ByteArrayInputStream(bytes);
		}
		catch (UnsupportedEncodingException err)
		{
			throw new RuntimeException( err ) ;
		}
	}

	public static void write(byte[] data, OutputStream output)
			throws IOException
	{
		if (data != null)
			output.write(data);
	}

	public static void write(byte[] data, Writer output) throws IOException
	{
		if (data != null)
			output.write(new String(data));
	}

	public static void write(byte[] data, Writer output, String encoding)
			throws IOException
	{
		if (data != null)
		{
			if (encoding == null)
				write(data, output);
			else
				output.write(new String(data, encoding));
		}
	}

	public static void write(char[] data, Writer output) throws IOException
	{
		if (data != null)
			output.write(data);
	}

	public static void write(char[] data, OutputStream output)
			throws IOException
	{
		if (data != null)
			output.write(new String(data).getBytes());
	}

	public static void write(char[] data, OutputStream output, String encoding)
			throws IOException
	{
		if (data != null)
		{
			if (encoding == null)
				write(data, output);
			else
				output.write(new String(data).getBytes(encoding));
		}
	}

	public static void write(String data, Writer output) throws IOException
	{
		if (data != null)
			output.write(data);
	}

	public static void write(String data, OutputStream output)
			throws IOException
	{
		if (data != null)
			output.write(data.getBytes());
	}

	public static void write(String data, OutputStream output, String encoding)
			throws IOException
	{
		if (data != null)
		{
			if (encoding == null)
				write(data, output);
			else
				output.write(data.getBytes(encoding));
		}
	}

	public static void write(StringBuffer data, Writer output)
			throws IOException
	{
		if (data != null)
			output.write(data.toString());
	}

	public static void write(StringBuffer data, OutputStream output)
			throws IOException
	{
		if (data != null)
			output.write(data.toString().getBytes());
	}

	public static void write(StringBuffer data, OutputStream output,
			String encoding) throws IOException
	{
		if (data != null)
		{
			if (encoding == null)
				write(data, output);
			else
				output.write(data.toString().getBytes(encoding));
		}
	}

	public static void writeLines(Collection<String> lines, String lineEnding,
			OutputStream output) throws IOException
	{
		if (lines == null)
			return;
		if (lineEnding == null)
			lineEnding = LINE_SEPARATOR;
		for (Iterator<String> it = lines.iterator(); it.hasNext();)
		{
			Object line = it.next();
			if (line != null)
				output.write(line.toString().getBytes());
			output.write(lineEnding.getBytes());
		}
	}

	public static void writeLines(Collection<String> lines, String lineEnding,
			OutputStream output, String encoding) throws IOException
	{
		if (encoding == null)
			writeLines(lines, lineEnding, output);
		else
		{
			if (lines == null)
				return;
			if (lineEnding == null)
				lineEnding = LINE_SEPARATOR;
			for (Iterator<String> it = lines.iterator(); it.hasNext();)
			{
				Object line = it.next();
				if (line != null)
					output.write(line.toString().getBytes(encoding));
				output.write(lineEnding.getBytes(encoding));
			}
		}
	}

	public static void writeLines(Collection<String> lines, String lineEnding,
			Writer writer) throws IOException
	{
		if (lines == null)
			return;
		if (lineEnding == null)
			lineEnding = LINE_SEPARATOR;
		for (Iterator<String> it = lines.iterator(); it.hasNext();)
		{
			Object line = it.next();
			if (line != null)
				writer.write(line.toString());
			writer.write(lineEnding);
		}
	}

	public static int copy(InputStream input, OutputStream output)
			throws IOException
	{
		byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
		int count = 0;
		int n = 0;
		while (-1 != (n = input.read(buffer)))
		{
			output.write(buffer, 0, n);
			count += n;
		}
		return count;
	}

	public static int copy(InputStream input, OutputStream output , int limit )
			throws IOException
	{
		byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
		int count = 0;
		int n = 0;
		int size = buffer.length<limit?buffer.length:limit ;

		while (-1 != (n = input.read(buffer,0,size )))
		{
			output.write(buffer, 0, n);
			count += n;
			limit -= n;
			size = buffer.length<limit?buffer.length:limit ;
			if( size <= 0 )
				break ;
		}
		return count;
	}

	public static void copy(InputStream input, Writer output)
			throws IOException
	{
		InputStreamReader in = new InputStreamReader(input);
		copy(in, output);
	}

	public static void copy(InputStream input, Writer output, String encoding)
			throws IOException
	{
		if (encoding == null)
			copy(input, output);
		else
		{
			InputStreamReader in = new InputStreamReader(input, encoding);
			copy(in, output);
		}
	}

	public static int copy(Reader input, Writer output) throws IOException
	{
		char[] buffer = new char[DEFAULT_BUFFER_SIZE];
		int count = 0;
		int n = 0;
		while (-1 != (n = input.read(buffer)))
		{
			output.write(buffer, 0, n);
			count += n;
		}
		return count;
	}

	public static void copy(Reader input, OutputStream output)
			throws IOException
	{
		OutputStreamWriter out = new OutputStreamWriter(output);
		copy(input, out);
		out.flush();
	}

	public static void copy(Reader input, OutputStream output, String encoding)
			throws IOException
	{
		if (encoding == null)
			copy(input, output);
		else
		{
			OutputStreamWriter out = new OutputStreamWriter(output, encoding);
			copy(input, out);
			out.flush();
		}
	}
	
	private static Executor ioExecutors = new ThreadPoolExecutor(10, 100, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>() ) ;
	
	public void setIoExecutors(Executor ioExecutors)
	{
		IOUtils.ioExecutors = ioExecutors;
	}

	public static void copy( final InputStream input, final OutputStream output, boolean async ) throws IOException
	{
		if( !async )
			copy(input, output) ;
		else
		{
			ioExecutors.execute( new Runnable()
			{
				@Override
				public void run()
				{
					try
					{
						copy( input, output ) ;
					}
					catch (IOException err)
					{
						err.printStackTrace();
					}
				}
			}) ;
		}
	}

	public static boolean contentEquals(InputStream input1, InputStream input2)
			throws IOException
	{
		if (!(input1 instanceof BufferedInputStream))
			input1 = new BufferedInputStream(input1);
		if (!(input2 instanceof BufferedInputStream))
			input2 = new BufferedInputStream(input2);

		int ch = input1.read();
		while (-1 != ch)
		{
			int ch2 = input2.read();
			if (ch != ch2)
				return false;
			ch = input1.read();
		}

		int ch2 = input2.read();
		return (ch2 == -1);
	}

	public static boolean contentEquals(Reader input1, Reader input2)
			throws IOException
	{
		if (!(input1 instanceof BufferedReader))
			input1 = new BufferedReader(input1);
		if (!(input2 instanceof BufferedReader))
			input2 = new BufferedReader(input2);

		int ch = input1.read();
		while (-1 != ch)
		{
			int ch2 = input2.read();
			if (ch != ch2)
				return false;
			ch = input1.read();
		}

		int ch2 = input2.read();
		return (ch2 == -1);
	}
}
